package com.gzxx.service.impl;

import com.gzxx.constant.RedisCacheConstant;
import com.gzxx.constant.SystemConstant;
import com.gzxx.domain.bo.SysUserBO;
import com.gzxx.domain.converter.SysUserConverter;
import com.gzxx.domain.dto.UserChangePwdDto;
import com.gzxx.domain.dto.SysLoginDto;
import com.gzxx.domain.vo.CaptchaVo;
import com.gzxx.domain.vo.IndexVo;
import com.gzxx.domain.vo.LoginVo;
import com.gzxx.domain.vo.SysMenuVo;
import com.gzxx.security.CurrentUser;
import com.gzxx.security.TokenService;
import com.gzxx.service.SysLoginService;
import com.gzxx.service.SysMenuService;
import com.gzxx.service.SysUserService;
import com.gzxx.util.*;
import com.redisdatastorage.cache.RedisCache;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author MAX
 * @date 2020/7/24 1:24 下午
 */

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SysLoginServiceImpl implements SysLoginService {

    private final AuthenticationManager authenticationManager;
    private final TokenService tokenService;
    private final RedisCache redisCache;

    private final SysMenuService sysMenuService;
    private final SysUserService sysUserService;

    @Override
    public ServerResponse login(SysLoginDto sysLoginDto) {
        try {
            //用户验证
            Authentication authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(sysLoginDto.getUsername(), sysLoginDto.getPassword()));

            CurrentUser loginUser = (CurrentUser) authentication.getPrincipal();
            String token = tokenService.createToken(loginUser);
            if (StringUtils.isEmpty(token)) {
                return ServerResponse.error("认证失败");
            }
            LoginVo loginVo = new LoginVo();
            loginVo.setToken(token);
            loginVo.setUserAccount(loginUser.getUserAccount());
            return ServerResponse.success(loginVo);
        } catch (AuthenticationException e) {
            log.info("Login user {} authentication failed", sysLoginDto.getUsername(), e);
            String msg = e.getMessage();
            if (e instanceof LockedException) {
                msg = "账户已禁用，请联系管理员!";
            }
            return ServerResponse.error(msg);
        }
    }

    /**
     * 首页
     */
    @Override
    public IndexVo index() {
        IndexVo indexVo = new IndexVo();
        CurrentUser currentUser = SysLoginUserHelper.getCurrentPrincipal();
        indexVo.setPermissions(currentUser.getPermissions());

        List<SysMenuVo> menuList = sysMenuService.getUserMenuList(currentUser.getId());
        indexVo.setMenuVos(menuList);
        return indexVo;
    }

    /**
     * 生成验证码
     */
    @Override
    public ServerResponse captcha() {
        // 生成随机字串
        String verifyCode = VerifyCodeUtils.generateVerifyCode(6);
        // 唯一标识
        String uuid = Identities.uuid();
        String verifyKey = RedisCacheConstant.CAPTCHA_KEY + uuid;
        // 放入redis 缓存
        redisCache.setObject(verifyKey, verifyCode, SystemConstant.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        // 生成图片
        int w = 111, h = 36;
        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            VerifyCodeUtils.outputImage(w, h, stream, verifyCode);
            CaptchaVo captchaVo = new CaptchaVo();
            captchaVo.setUuid(uuid);
            captchaVo.setImage("data:image/gif;base64," + Base64Utils.encode(stream.toByteArray()));
            return ServerResponse.success(captchaVo);
        } catch (Exception e) {
            log.error("生成验证码失败: {}", e);
            return ServerResponse.error(e.getMessage());
        }
    }

    @Override
    public ServerResponse changePwd(UserChangePwdDto dto) {
        CurrentUser user = SysLoginUserHelper.getCurrentPrincipal();
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
            return ServerResponse.error("原密码不正确，请确认");
        }
        if (dto.getNewPassword().equals(dto.getPassword())) {
            return ServerResponse.error("新密码与原密码相同");
        }
        SysUserBO userBO = SysUserConverter.MAPPER.convertChangePwd(dto);
        boolean isSuccess = sysUserService.changePwd(userBO);
        return isSuccess ? ServerResponse.success() : ServerResponse.error("修改密码失败");
    }

    /**
     * 系统退出
     *
     * @param request
     * @return
     */
    @Override
    public void logout(HttpServletRequest request) {
        tokenService.removeToken(request);
    }
}
